#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <argz.h>

#define SERVER "/tmp/videlsock"
#define MAXBUFLEN 200

int string_cmp(const void *l, const void *r)
{
    const char * left  = (const char *)l;
    const char * right = (const char *)r;

    return strcmp(left, right);
}

void print_argz(char * argz, size_t argz_len)
{
    char * entry;
    size_t i,n;
    i = 0;
    n = argz_count(argz, argz_len);

    char * tab[n];
    entry = NULL;
    while ((entry = argz_next(argz, argz_len, entry))) {
        tab[i] = strdup(entry);
        i += 1;
    }

    qsort(tab, n, sizeof(char*), string_cmp);

    for(i = 0; i < n; ++i)
        printf("   %s\n", tab[i]);
}

void addInterest(int unixfd, char * str)
{
    char * buf;
    size_t buf_len;
    ssize_t bytes_written, bytes_read;

    printf("[Info] Przesyłam zainteresowanie: '%s'...\n", str);

    buf = calloc(MAXBUFLEN, sizeof(char));

    buf[0] = 'a';
    strcpy(buf+1, str);

    buf_len = 1 + strlen(str) + 1;
    bytes_written = write(unixfd, buf, buf_len);

    if (bytes_written < 0) {
        perror("addInterest write");
    }

    bytes_read = read(unixfd, buf, MAXBUFLEN);

    if (bytes_read > 0) {
        printf("Current set of interests:\n");
        print_argz(buf, bytes_read);
    } else {
        perror("addInterest read");
    }

    free(buf);
}

void list(int unixfd)
{
    char msg = 'l';
    if (send(unixfd, &msg, 1, 0) == -1) {
        perror("[ERROR] Nie przesłano\n");
        close(unixfd);
        exit(EXIT_FAILURE);
    }

    size_t  buf_len = 1024 * 1024;
    char * buf = calloc(1, buf_len);
    ssize_t bytes_read;
    bytes_read = read(unixfd, buf, buf_len);

    printf("%s", buf);

    close(unixfd);
}

void deleteInterest(int sockfd, char * str)
{
    char * buf;
    size_t buf_len;
    ssize_t bytes_written, bytes_read;

    buf_len = 1 + strlen(str) + 1;
    buf = calloc(buf_len, sizeof(char));

    buf[0] = 'd';
    strcpy(buf+1, str);

    bytes_written = write(sockfd, buf, buf_len);

    if (bytes_written < 0) {
        perror("deleteInterest write");
    }


    char rbuf[1000];
    bytes_read = read(sockfd, rbuf, MAXBUFLEN);

    if (bytes_read > 0) {
        printf("Current set of interests:\n");
        print_argz(rbuf, bytes_read);
    } else {
        printf("pwzd nie odpowiedział.");
    }
}

void info(int sockfd, char * str)
{
    char * buf;
    size_t buf_len;
    ssize_t bytes_written, bytes_read;

    buf_len = 1 + strlen(str) + 1;
    buf     = calloc(buf_len, sizeof(char));

    buf[0] = 'i';
    strcpy(buf+1, str);

    bytes_written = write(sockfd, buf, buf_len);

    if (bytes_written < 0) {
        perror("info write");
    }

    size_t rbuf_len = 1024 * 1024;
    char * rbuf = malloc(rbuf_len * sizeof(char)); // zaalokujemy sobie całego megaska, a co!
    bytes_read = read(sockfd, rbuf, rbuf_len);


    if (bytes_read > 1) {
        print_argz(rbuf, bytes_read);
    }
    else if (bytes_read == 1) {
        printf("No one is interested in it YOU PERVERT!\n");
    } else {
        printf("pwzd nie odpowiedział.");
    }

    free(buf);
    free(rbuf);
}

void quit(int unixfd)
{
    printf("[INFO] Wysyłam sygnał zatrzymania\n");

    char msg = 'q';
    if (send(unixfd, &msg, 1, 0) == -1) {
        printf("[ERROR] Nie przesłano\n");
        close(unixfd);
        exit(EXIT_FAILURE);
    }

    // wypisz informację zwrotną
    socklen_t len;
    char buf[MAXBUFLEN];
    while((len = recv(unixfd, &buf, MAXBUFLEN, 0) > 0)) {
        printf("pwzd: `%s`", buf);
    }
    printf("\n");

    close(unixfd);
}

int main(int argc, char * argv[])
{
    struct sockaddr_un server;
    //char buf[MAXBUFLEN];

    int unixfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (unixfd == -1) {
        perror("pwz: socket");
        exit(1);
    }

    server.sun_family = PF_LOCAL;
    strcpy(server.sun_path, SERVER);

    if (connect(unixfd, (struct sockaddr*)&server, sizeof(struct sockaddr_un)) == -1) {
        close(unixfd);
        perror("pwzd nie jest uruchomiony.\n\tconnect");
        exit(EXIT_FAILURE);
    }

    int c;
    optarg = 0;

    while ((c = getopt(argc, argv, "a:d:i:q")) != -1) {
        char * cvalue = optarg;
        switch (c) {
        case 'a':
            addInterest(unixfd, cvalue);
            break;
        case 'd':
            deleteInterest(unixfd, cvalue);
            break;
        case 'i':
            info(unixfd, cvalue);
            break;
        case 'q':
            quit(unixfd);
            break;
        case '?':
            if (optopt == 'a' || optopt == 'd' || optopt =='i')
                fprintf(stderr, "Opcja -%c wymaga argumentu.\n", optopt);
            else if (isprint(optopt))
                fprintf(stderr, "Nieznana opcja -%c\n", optopt);
            else
                fprintf(stderr, "Nieznany znak opcji `\\x%x'.", optopt);
            return 1;
        default:
            exit(1);
        }
    }
    list(unixfd);
    // close(sockfd);
    return EXIT_SUCCESS;
}
